﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Common;
using System.Data.SqlTypes;
using System.Diagnostics;
using System.Text;


namespace DbManager
{
    using Data;
    using Mapping;

    public class DbManager : Component
    {

        #region Constructors

        public DbManager() { }
        static DbManager()
        {
            AddDataProvider(new MySqlDataProvider());
            AddDataProvider(new MSSqlServerDataProvider());
        }

        public DbManager(DataProvider dataProvider, string connectionString)
        {
            if (dataProvider == null) throw new ArgumentNullException("dataProvider");
            if (connectionString == null) throw new ArgumentNullException("connectionString");

            _dataProvider = dataProvider;
            _connection = dataProvider.CreateConnection();

            _connection.ConnectionString = connectionString;
        }

        public DbManager(DataProvider dataProvider, IDbConnection connection)
        {
            if (dataProvider == null) throw new ArgumentNullException("dataProvider");
            if (connection == null) throw new ArgumentNullException("connection");

            _dataProvider = dataProvider;
            _connection = connection;
        }

        #endregion 

        #region Public Properties

        private DataProvider _dataProvider;
        public DataProvider DataProvider
        {
            get { return _dataProvider; }
            protected set { _dataProvider = value; }
        }

        #endregion 

        #region Connection

        private IDbConnection _connection;
        public IDbConnection Connection
        {
            get
            {
                if (_connection.State == ConnectionState.Closed)
                    OpenConnection();
                return _connection;
            }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
                _connection = value;
            }
        }

        private void OpenConnection()
        {
            try
            {
                _connection.Open();
            }
            catch (System.Exception e )
            {
                String error = e.Message;
            }
        }
         

        public string GetConnectionString()
        {
            return _connection.ConnectionString;
        }
        
        #endregion
        

        //#region Set Command

        //public DbManager SetCommand(
        //    string commandText)
        //{
        //    return SetCommand(CommandType.Text, commandText, null);
        //}

        //private DbManager SetCommand(
        //    CommandType commandType,
        //    string commandText,
        //    params        IDbDataParameter[] commandParameters)
        //{
        //    return this;
        //}

        //#endregion 

        //#region Execute List

        //public List<T> ExecuteList<T>()
        //{
        //    List<T> list = new List<T>();
        //    ExecuteListInternal<T>(list, null);
        //    return list;
        //}

        //private void ExecuteListInternal<T>(IList<T> list, params object[] parameters)
        //{
        //    if (list == null)
        //        list = new List<T>();

        //    //if (_prepared)
        //    //    InitParameters(CommandAction.Select);

        //    //using (IDataReader dr = ExecuteReaderInternal())
        //    //{
        //    //    _mappingSchema.MapDataReaderToList<T>(dr, list, parameters);
        //    //}
        //}

        //#endregion 

        private MappingSchema _mappingSchema;// = Map.DefaultSchema;
        public MappingSchema MappingSchema
        {
            get { return _mappingSchema; }
            set { _mappingSchema = value /*?? Map.DefaultSchema*/; }
        }


        static readonly Dictionary<string, DataProvider> _dataProviderNameList = new Dictionary<string, DataProvider>(8, StringComparer.OrdinalIgnoreCase);
        static readonly Dictionary<Type, DataProvider> _dataProviderTypeList = new Dictionary<Type, DataProvider>(4);
        static readonly object _dataProviderListLock = new object();
        public static void AddDataProvider(DataProvider dataProvider)
        {
            if (null == dataProvider)
                throw new ArgumentNullException("dataProvider");

            if (string.IsNullOrEmpty(dataProvider.UniqueName))
                throw new ArgumentException("InvalidDataProviderUniqueName");

            if (string.IsNullOrEmpty(dataProvider.ProviderName))
                throw new ArgumentException("InvalidDataProviderProviderName");

            if (dataProvider.ConnectionType == null || !typeof(IDbConnection).IsAssignableFrom(dataProvider.ConnectionType))
                throw new ArgumentException("InvalidDataProviderConnectionType");

            lock (_dataProviderListLock)
            {
                _dataProviderNameList[dataProvider.UniqueName.ToUpper()] = dataProvider;
                _dataProviderNameList[dataProvider.ProviderName] = dataProvider;
                _dataProviderTypeList[dataProvider.ConnectionType] = dataProvider;
            }
        }

        #region Dispose

        protected override void Dispose(bool disposing)
        {
            if (disposing)
                Close();

            base.Dispose(disposing);
        }

        public event EventHandler OnClosing;
        public event EventHandler OnClosed;
        public void Close()
        {
            if (OnClosing != null)
                OnClosing(this, EventArgs.Empty);

            if (_connection != null)
            {
                ExecuteOperation(OperationType.CloseConnection, _connection.Dispose);
                _connection = null;
            }

            if (OnClosed != null)
                OnClosed(this, EventArgs.Empty);

        }

        private void ExecuteOperation(OperationType operationType, Action operation)
        {
            try
            {
                operation();
            }
            catch (Exception)
            {
                throw;
            }
        }

        #endregion

    }
}
